import os
import time
import json
import curses
import urllib

from shared.colors import Color
from shared.monitor import Monitor


class Domain:
	"""Individual domain monitor"""
	decoder = json.JSONDecoder()

	def __init__(self, domain, api_url, window):
		self._window = window
		self._window_size = self._window.getmaxyx()
		self._window_pos = self._window.getbegyx()
		self._container = self._window.derwin(self._window_size[0] - 1, self._window_size[1], 1, 0)
		self._container_size = self._container.getmaxyx()

		self._url = api_url
		self._domain = domain
		self._offset = 0
		self._direction = 1
		self._data = []

		# configure window
		self.update_window()

	def _get_params(self, string):
		"""Get color, char and string for displaying"""
		change_up = string[0] == '+'

		if string[0] in ('-', '+'):
			string = string[1:]
			change_char = curses.ACS_UARROW if change_up else curses.ACS_DARROW
			change_color = Color.INCREASE if change_up else Color.DECREASE

		else:
			change_char = ' '
			change_color = 0

		return string, change_char, change_color

	def update_window(self):
		"""Repaint the whole window"""
		# set window background color
		self._window.bkgd(curses.color_pair(Color.WINDOW))
		self._container.bkgd(curses.color_pair(Color.CONTAINER))

		# print text
		self._window.addstr(0, 0, self._domain, curses.color_pair(Color.WINDOW) | curses.A_BOLD)
		self._window.addstr(0, 29, 'Gog', curses.color_pair(Color.HEADER) | curses.A_BOLD)
		self._window.addstr(0, 33, 'Bng', curses.color_pair(Color.HEADER) | curses.A_BOLD)
		self._window.addstr(0, 37, 'Yah', curses.color_pair(Color.HEADER) | curses.A_BOLD)
		self._window.addstr(0, 42, 'Day', curses.color_pair(Color.HEADER) | curses.A_BOLD)
		self._window.addstr(0, 49, 'Week', curses.color_pair(Color.HEADER) | curses.A_BOLD)
		self._window.addstr(0, 56, 'Month', curses.color_pair(Color.HEADER) | curses.A_BOLD)

		# update container
		self.update_container()

		# mark window as updated
		self._window.noutrefresh()

	def update_container(self):
		"""Update container content"""
		sub_data = self._data[self._offset : self._offset + self._container_size[0]]

		# update offset
		if len(self._data) > self._container_size[0]:
			self._offset += self._direction
			if self._offset + self._container_size[0] > len(self._data) and self._direction == 1:
				self._offset = len(self._data) - self._container_size[0] - 1
				self._direction = -1

			elif self._offset - 1 < 0 and self._direction == -1:
				self._offset = 0
				self._direction = 1

		# update container
		for index, item in enumerate(sub_data, 0):
			change_day = self._get_params(item['day'])
			change_week = self._get_params(item['week'])
			change_month = self._get_params(item['month'])

			rank_google = item['google'] if item['google'] != '0' else ''
			rank_bing = item['bing'] if item['bing'] != '0' else ''
			rank_yahoo = item['yahoo'] if item['yahoo'] != '0' else ''

			self._container.addstr(index, 0, item['keywords'][:27].ljust(27))
			self._container.addstr(index, 29, rank_google.ljust(3))
			self._container.addstr(index, 33, rank_bing.ljust(3))
			self._container.addstr(index, 37, rank_yahoo.ljust(3))
			self._container.addstr(index, 42, change_day[0].rjust(3), curses.color_pair(change_day[2]))
			self._container.addch(index, 46, change_day[1], curses.color_pair(change_day[2]))
			self._container.addstr(index, 49, change_week[0].rjust(3), curses.color_pair(change_week[2]))
			self._container.addch(index, 53, change_week[1], curses.color_pair(change_week[2]))
			self._container.addstr(index, 56, change_month[0].rjust(3), curses.color_pair(change_month[2]))
			self._container.addch(index, 60, change_month[1], curses.color_pair(change_month[2]))

		self._container.noutrefresh()

	def update_content(self):
		"""Get updated values for domain from SerpBook"""
		self._data = []

		# load and parse JSON from provider
		url_handler = urllib.urlopen(self._url)
		data = Domain.decoder.decode(url_handler.read())

		# only append data we are interested in
		for row in data:
			self._data.append({
					'keywords': row['KW'],
					'google': row['grank'].strip(),
					'bing': row['brank'].strip(),
					'yahoo': row['yrank'].strip(),
					'day': row['day'].strip(),
					'week': row['week'].strip(),
					'month': row['month'].strip()
				})


class SeoMonitor(Monitor):
	"""SEO monitor using SerpBook API as source."""

	def __init__(self, window, config_file):
		Monitor.__init__(self, window, config_file)

		self._child_size = (11, 62)
		self._child_spacing = (1, 2)
		self._next_position = (1, 0)
		self._domains = []
		self._last_update = ''

	def run(self):
		"""Main application loop"""
		Monitor.run(self)

		# create domain monitors
		self._create_domains()

		# load data from servers
		self._update_all_domains()

		counter = 0
		while self._thread_active.is_set():
			# update data every 15 minutes
			counter += 3
			if counter >= 900:
				self._update_all_domains()
				counter = 0

			# update screen
			if self._visible:
				map(lambda domain: domain.update_container(), self._domains)
				self._window.noutrefresh()

			# sleep for a while
			time.sleep(3)

	def _update_all_domains(self):
		"""Update all domain data"""
		time_format = '%a, %d %b %Y %H:%M:%S'
		self._last_update = time.strftime(time_format, time.localtime())

		# update window content
		self._update_timestamp()
		map(lambda domain: domain.update_content(), self._domains)

	def _get_next_child_position(self):
		"""Calculate position of next child window"""
		result = self._next_position
		window_size = self._window.getmaxyx()

		if self._next_position is not None:
			# next position is to the right of the current one
			self._next_position = (self._next_position[0], self._next_position[1] + self._child_size[1] + self._child_spacing[1])

			# wrap child to next row
			if self._next_position[1] + self._child_size[1] > window_size[1]:
				self._next_position = (self._next_position[0] + self._child_size[0] + self._child_spacing[0], 0)

			# break the loading if there's no space left
			if self._next_position[0] + self._child_size[0] > window_size[0]:
				self._next_position = None

		return result

	def _create_domains(self):
		"""Create domain monitors"""
		data = []

		# load JSON config and parse it
		if os.path.exists(self._config_file):
			with open(self._config_file, 'r') as seo_data:
				data = Domain.decoder.decode(seo_data.read())

		# create objects
		for item in data:
			# get window position
			window_position = self._get_next_child_position()
			
			if window_position is not None:
				new_window = self._window.derwin(*(self._child_size + window_position))
				self._domains.append(Domain(item['domain'], item['url'], new_window))

			else:
				# that's as many windows as we can show
				break

	def _update_timestamp(self):
		"""Paint timestamp on top of the window"""
		self._window.addstr(0, 0, 'Last updated: {0}'.format(self._last_update).ljust(60))

	def show(self):
		"""Show monitor windows"""
		Monitor.show(self)
		self._update_timestamp()
		map(lambda domain: domain.update_window(), self._domains)

	def hide(self):
		"""Hide monitor windows"""
		Monitor.hide(self)
